﻿using System;
using System.Runtime.InteropServices;
using System.Text;

namespace IupNET
{
   
    public delegate void HardcopyCallback(Canvas canvas_copy);
    

    public class Canvas:IDisposable
    {
        internal IntPtr cobj;
        internal const int Query = unchecked((int)0xffffffff);
        private bool disposed = false;
        HardcopyCallback hardcopy_callback;


        #region CANVAS_GENERAL
        public Canvas(Context context, string data)
        {
            cobj = Cdc.CreateCanvasStr(context.context,data);
        }

        public Canvas(Context context, IntPtr data)
        {
            cobj = Cdc.CreateCanvasPtr(context.context,data);
        }

        public Canvas(Context context, Canvas data)
        {
            cobj = Cdc.CreateCanvasPtr(context.context, data.cobj);
        }

        public Canvas(IupBase i)
        {
            if (i is PaintArea)
                cobj = Cdc.CreateCanvasPtr(Context.Iup.context, i.cobject);
            else
                throw new Exception("IUPControl used to create canvas has to be a PaintArea");
        }

        private Canvas(IntPtr cobj)
        {
            //private constructor, used by hardcopy (creates a Canvas object from a c/c++ canvas object)
            
            this.cobj = cobj;
        }

        ~Canvas()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }


        public void Dispose(bool disposing)
        {
            
            if (!disposed)
            {
                if (cobj != IntPtr.Zero)
                {
                    Cdc.KillCanvas(cobj);
                    cobj = IntPtr.Zero;
                }
            }
            disposed = true;
        
        }

        
        public void Activate()
        {
            Cdc.CanvasActivate(cobj);
        }

        public void Deactivate()
        {
            Cdc.CanvasDeactivate(cobj);
        }

        public Context Context
        {
            get
            {
                return new Context(Cdc.CanvasGetContext(cobj));
            }
        }

        public void Simulate(SimualtionFlags flags)
        {
            Cdc.CanvasSimulate(cobj, (int)flags);
        }

        public void Clear()
        {
            Cdc.CanvasClear(cobj);
        }


        public void Flush()
        {
            Cdc.CanvasFlush(cobj);
        }

        public CanvasState SaveState()
        {
            return new CanvasState(Cdc.CanvasSaveState(cobj));
        }

        public void RestoreState(CanvasState state) {
            IntPtr o=state.saveobj;
            if(o==IntPtr.Zero)
                throw new Exception("RestoreSave() with disposed/invalid CanvasState");
            Cdc.CanvasRestoreState(cobj,o);
        }

        public void SetAttribute(string name, string data)
        {
            Cdc.CanvasSetAttribute(cobj, name, data);
        }

        public string GetAttribute(string name)
        {
            return Cdc.CanvasGetAttribute(cobj, name);
        }

        public int Foreground
        {
            get
            {
                return Cdc.CanvasForeground(cobj, Query);
            }
            set
            {
                Cdc.CanvasSetForeground(cobj, value);

            }
        }

        public int Background
        {
            get
            {
                return Cdc.CanvasBackground(cobj, Query);
            }
            set
            {
                Cdc.CanvasSetBackground(cobj, value);
            }
        }

        public WriteMode WriteMode {
            get {
                return (WriteMode)Cdc.CanvasWriteMode(cobj,Query);
            }
            set {
                Cdc.CanvasWriteMode(cobj,(int)value);
            }
        }

        #endregion

        #region LINE_PRIMITIVES

        public void Line(int x1, int y1, int x2, int y2)
        {
            Cdc.CanvasLine(cobj, x1, y1, x2, y2);
        }

        public void Line(double x1, double y1, double x2, double y2)
        {
            Cdc.FCanvasLine(cobj, x1, y1, x2, y2);
        }

        public void LineW(double x1, double y1, double x2, double y2)
        {
            Cdc.WCanvasLine(cobj, x1, y1, x2, y2);
        }

        public void Begin(BeginMode mode)
        {
            Cdc.CanvasBegin(cobj, (int)mode);
        }

        public void Vertex(int x, int y)
        {
            Cdc.CanvasVertex(cobj, x, y);
        }

        public void Vertex(double x, double y)
        {
            Cdc.FCanvasVertex(cobj,x, y);
        }

        public void VertexW(double x, double y)
        {
            Cdc.WCanvasVertex(cobj, x, y);
        }

        public void End()
        {
            Cdc.CanvasEnd(cobj);
        }

        public void Rect(int xmin, int xmax, int ymin, int ymax)
        {
            Cdc.CanvasRect(cobj, xmin, xmax, ymin, ymax);
        }

        public void Rect(double xmin, double xmax, double ymin, double ymax)
        {
            Cdc.FCanvasRect(cobj, xmin, xmax, ymin, ymax);
        }

        public void RectW(double xmin, double xmax, double ymin, double ymax)
        {
            Cdc.WCanvasRect(cobj, xmin, xmax, ymin, ymax);
        }

        public void Arc(int centerx, int centery, int width, int height, double angle1, double angle2)
        {
            Cdc.CanvasArc(cobj, centerx, centery, width, height, angle1, angle2);
        }

        public void Arc(double centerx, double centery, double width, double height, double angle1, double angle2)
        {
            Cdc.FCanvasArc(cobj, centerx, centery, width, height, angle1, angle2);
        }

        public void ArcW(double centerx, double centery, double width, double height, double angle1, double angle2)
        {
            Cdc.WCanvasArc(cobj, centerx, centery, width, height, angle1, angle2);
        }

        public LineStyle LineStyle
        {
            get
            {
                return (LineStyle)Cdc.CanvasLineStyle(cobj, Query);
            }
            set
            {
                Cdc.CanvasLineStyle(cobj, (int)value);
            }
        }

        public int[] LineStyleDashes
        {
            set
            {
                Cdc.CanvasLineStyleDashes(cobj, ref value[0], value.Length);
            }
        }

        public int LineWidth
        {
            get
            {
                return Cdc.CanvasLineWidth(cobj, Query);
            }
            set
            {
                Cdc.CanvasLineWidth(cobj, value);
            }
        }

        public double LineWidthW
        {
            get
            {
                return Cdc.WCanvasLineWidth(cobj, Query);
            }
            set
            {
                Cdc.WCanvasLineWidth(cobj, value);
            }
        }

        public LineJoin LineJoin
        {
            get
            {
                return (LineJoin)Cdc.CanvasLineJoin(cobj, Query);
            }
            set
            {
                Cdc.CanvasLineJoin(cobj, (int)value);
            }
        }

        public LineCap LineCap
        {
            get
            {
                return (LineCap)Cdc.CanvasLineCap(cobj, Query);
            }

            set
            {
                Cdc.CanvasLineCap(cobj, (int)value);
            }
        }

        
        #endregion

        #region FILLED_PRIMITIVES

        public void Box(int xmin, int xmax, int ymin, int ymax)
        {
            Cdc.CanvasBox(cobj, xmin, xmax, ymin, ymax);
        }

        public void Box(double xmin, double xmax, double ymin, double ymax)
        {
            Cdc.FCanvasBox(cobj, xmin, xmax, ymin, ymax);
        }

        public void BoxW(double xmin, double xmax, double ymin, double ymax)
        {
            Cdc.WCanvasBox(cobj, xmin, xmax, ymin, ymax);
        }

        public void Sector(int xc, int yc, int w, int h, double angle1, double angle2)
        {
            Cdc.CanvasSector(cobj, xc, yc, w, h, angle1, angle2);
        }

        public void Sector(double xc, double yc, double w, double h, double angle1, double angle2)
        {
            Cdc.FCanvasSector(cobj, xc, yc, w, h, angle1, angle2);
        }

        public void SectorW(double xc, double yc, double w, double h, double angle1, double angle2)
        {
            Cdc.WCanvasSector(cobj, xc, yc, w, h, angle1, angle2);
        }

        public void Chord(int xc, int yc, int w, int h, double angle1, double angle2)
        {
            Cdc.CanvasChord(cobj, xc, yc, w, h, angle1, angle2);
        }

        public void Chord(double xc, double yc, double w, double h, double angle1, double angle2)
        {
            Cdc.FCanvasChord(cobj, xc, yc, w, h, angle1, angle2);
        }

        public void ChordW(double xc, double yc, double w, double h, double angle1, double angle2)
        {
            Cdc.WCanvasChord(cobj, xc, yc, w, h, angle1, angle2);
        }

        public BackOpacity BackOpacity
        {
            get
            {
                return (BackOpacity)Cdc.CanvasBackOpacity(cobj, Query);
            }
            set
            {
                Cdc.CanvasBackOpacity(cobj, (int)value);
            }
        }

        public FillMode FillMode
        {
            get
            {
                return (FillMode)Cdc.CanvasFillMode(cobj, Query);
            }
            set
            {
                Cdc.CanvasFillMode(cobj, (int)value);
            }
        }

        public InteriorStyle InteriorStyle
        {
            get
            {
                return (InteriorStyle)Cdc.CanvasInteriorStyle(cobj, Query);
            }
            set
            {
                Cdc.CanvasInteriorStyle(cobj, (int)value);
            }
        }

        public Hatch Hatch
        {
            get
            {
                return (Hatch)Cdc.CanvasHatch(cobj, Query);
            }
            set
            {
                Cdc.CanvasHatch(cobj, (int)value);
            }
        }

        public void SetStipple(int w, int h, byte[] stipple)
        {
            Cdc.CanvasStipple(cobj, w, h, ref stipple[0]);
        }

        public void SetStippleW(int w, int h, byte[] pattern, double w_mm, double h_mm)
        {
            Cdc.WCanvasStipple(cobj, w, h, ref pattern[0], w_mm, h_mm);
        }

        public byte[] GetStipple(out int w,out int h)
        {
            IntPtr ipat=Cdc.CanvasGetStipple(cobj,out w,out h);
            if (ipat == IntPtr.Zero)
                 return null;
            
            byte[] res=new byte[w*h];
            Marshal.Copy(ipat, res, 0, w * h);
            return res;
        }

        public void SetPattern(int w, int h, int[] pattern)
        {
            Cdc.CanvasPattern(cobj, w, h, ref pattern[0]);
        }

        public void SetPatternW(int w, int h, int[] pattern, double w_mm, double h_mm)
        {
            Cdc.WCanvasPattern(cobj, w, h, ref pattern[0], w_mm, h_mm);
        }

        public int[] GetPattern(out int w, out int h)
        {
            IntPtr ipat = Cdc.CanvasGetPattern(cobj, out w, out h);
            if (ipat == IntPtr.Zero)
                return null;

            int[] res = new int[w * h];
            Marshal.Copy(ipat, res, 0, w * h);
            return res;
        }

        #endregion

        #region TEXT_PRIMITIVES
        public void Text(int x, int y, string text)
        {
            Cdc.CanvasText(cobj, x, y, text);
        }

        public void Text(double x, double y, string text)
        {
            Cdc.FCanvasText(cobj, x, y, text);
        }

        public void TextW(double x, double y, string text)
        {
            Cdc.WCanvasText(cobj, x, y, text);
        }

        public void SetFont(string typeface, FontStyle style, int size)
        {
            Cdc.CanvasFont(cobj, typeface, (int)style, size);
        }

        public void SetFontW(string typeface, FontStyle style, double size)
        {
            Cdc.WCanvasFont(cobj, typeface, (int)style, size);
        }

        public void GetFont(out string typeface,out FontStyle style,out int size)
        {
            StringBuilder sb=new StringBuilder(255);
            int s;
            Cdc.CanvasGetFont(cobj, sb, out s, out size);
            style = (FontStyle)s;
            typeface = sb.ToString();
        }


        public void GetFontW(out string typeface, out FontStyle style, out double size)
        {
            StringBuilder sb = new StringBuilder(255);
            int s;
            Cdc.WCanvasGetFont(cobj, sb, out s, out size);
            style = (FontStyle)s;
            typeface = sb.ToString();
        }

        public string NativeFont
        {
            get
            {
                return Cdc.CanvasNativeFontPtr(cobj, IntPtr.Zero);
            }

            set
            {
                Cdc.CanvasNativeFont(cobj, value);
            }
        }


        public TextAlignment TextAlignment
        {
            get
            {
                return (TextAlignment)Cdc.CanvasTextAlignment(cobj, Query);
            }
            set
            {
                Cdc.CanvasTextAlignment(cobj, (int)value);
            }
        }

        public double TextOrientation
        {
            get
            {
                return Cdc.CanvasTextOrientation(cobj, (double)Query);
            }
            set
            {
                Cdc.CanvasTextOrientation(cobj, value);
            }
        }

        public void GetFontDim(out int max_width, out int height, out int ascent, out int descent)
        {
            Cdc.CanvasGetFontDim(cobj, out max_width, out height, out ascent, out descent);
        }


        public void GetFontDimW(out double max_width, out double height, out double ascent, out double descent)
        {
            Cdc.WCanvasGetFontDim(cobj, out max_width, out height, out ascent, out descent);
        }

        public void GetTextSize(string text, out int w, out int h)
        {
            Cdc.CanvasGetTextSize(cobj, text, out w, out h);
        }

        public void GetTextSizeW(string text, out double w, out double h)
        {
            Cdc.WCanvasGetTextSize(cobj, text, out w,out h);
        }

        public int[] GetTextBounds(int x, int y, string text)
        {
            int[] rect = new int[8];
            Cdc.CanvasGetTextBounds(cobj, x, y, text, rect);
            return rect;
        }

        public double[] GetTextBoundsW(double x, double y, string text)
        {
            double[] rect = new double[8];
            Cdc.WCanvasGetTextBounds(cobj, x, y, text, rect);
            return rect;
        }

        public void GetTextBox(int x, int y, string text, out int xmin, out int xmax, out int ymin, out int ymax)
        {
            Cdc.CanvasGetTextBox(cobj, x, y, text, out xmin, out xmax, out ymin, out ymax);
        }


        public void GetTextBoxW(double x, double y, string text, out double xmin, out double xmax, out double ymin, out double ymax)
        {
            Cdc.WCanvasGetTextBox(cobj, x, y, text, out xmin, out xmax, out ymin, out ymax);
        }


        #endregion

        #region PIXELS_AND_MARKS

        public void Pixel(int x, int y, int color)
        {
            Cdc.CanvasPixel(cobj, x, y, color);
        }


        public void PixelW(double x, double y, int color)
        {
            Cdc.WCanvasPixel(cobj, x, y, color);
        }

        public void Mark(int x, int y)
        {
            Cdc.CanvasMark(cobj, x, y);
        }

        public void MarkW(double x, double y)
        {
            Cdc.WCanvasMark(cobj, x, y);
        }


        public MarkType MarkType
        {
            get
            {
                return (MarkType)Cdc.CanvasMarkType(cobj, Query);
            }
            set
            {
                Cdc.CanvasMarkType(cobj, (int)value);
            }
        }

        public int MarkSize
        {
            get
            {
                return Cdc.CanvasMarkSize(cobj, Query);
            }
            set
            {
                Cdc.CanvasMarkSize(cobj, value);
            }
        }


        public double MarkSizeW
        {
            get
            {
                return Cdc.WCanvasMarkSize(cobj, Query);
            }
            set
            {
                Cdc.WCanvasMarkSize(cobj, value);
            }
        }



        

        #endregion

        #region VECTOR_TEXT_PRIMITIVES

        public void VectorText(int x, int y, string text)
        {
            Cdc.CanvasVectorText(cobj, x, y, text);
        }

        public void VectorTextW(double x, double y, string text)
        {
            Cdc.WCanvasVectorText(cobj, x, y, text);
        }

        public void SetVectorTextDirection(int x1, int y1, int x2, int y2)
        {
            Cdc.CanvasVectorTextDirection(cobj, x1, y1, x2, y2);
        }

        public void SetVectorTextDirectionW(double x1, double y1, double x2, double y2)
        {
            Cdc.WCanvasVectorTextDirection(cobj, x1, y1, x2, y2);
        }

        public Matrix2d VectorTextTransform
        {
            //TODO: try if the conversion order is correct when bug about setting vector text transform is resolved

            get
            {
                //for some odd reason text uses another order of axis in matrix, we convert to our
                //standrad format
                IntPtr pd=Cdc.CanvasVectorTextTransformPtr(cobj, IntPtr.Zero);
                double[] m=new double[6];
                Marshal.Copy(pd, m, 0, 6);
                return new Matrix2d(m[0], m[3], m[1], m[4], m[2], m[5]);
            }
            set
            {
                double[] s = value.M;
                double[] d = new double[6];
                d[0] = s[0];
                d[1] = s[2];
                d[2] = s[4];
                d[3] = s[1];
                d[4] = s[3];
                d[5] = s[5];
                Cdc.CanvasVectorTextTransform(cobj, ref d[0]);
            }
        }

        public void SetVectorTextSize(int width, int height, string text)
        {
            Cdc.CanvasVectorTextSize(cobj, width, height, text);
        }

        public void SetVectorTextSizeW(double width, double height, string text)
        {
            Cdc.WCanvasVectorTextSize(cobj, width, height, text);
        }

        public int SetVectorCharSize
        {
            get
            {
                return Cdc.CanvasVectorCharSize(cobj, Query);
            }
            set
            {
                Cdc.CanvasVectorCharSize(cobj, value);
            }
        }

        public double SetVectorCharSizeW
        {
            get
            {
                return Cdc.WCanvasVectorCharSize(cobj, Query);
            }
            set
            {
                Cdc.WCanvasVectorCharSize(cobj, value);
            }
        }

        public void SetVectorFontSize(double size_x, double size_y)
        {
            Cdc.CanvasVectorFontSize(cobj, size_x, size_y);
        }

        public void SetGetVectorFontSize(out double size_x, out double size_y)
        {
            Cdc.CanvasGetVectorFontSize(cobj, out size_x, out size_y);
        }

        public string VectorFont
        {
            
            set
            {
                Cdc.CanvasVectorFont(cobj, value);
            }
        }

        public void GetVectorTextSize(string text, out int width, out int height)
        {
            Cdc.CanvasGetVectorTextSize(cobj, text, out width, out height);
        }

        public void GetVectorTextSizeW(string text, out double width, out double height)
        {
            Cdc.WCanvasGetVectorTextSize(cobj, text, out width, out height);
        }


        public int[] GetTextBounds(string text, int x, int y)
        {
            int[] b = new int[8];
            Cdc.CanvasGetVectorTextBounds(cobj, text, x, y, b);
            return b;
        }

        public double[] GetTextBoundsW(string text, double x, double y)
        {
            double[] b = new double[8];
            Cdc.WCanvasGetVectorTextBounds(cobj, text, x, y, b);
            return b;
        }

        public void GetVectorTextBox(int x, int y, string text, out int xmin, out int xmax, out int ymin, out int ymax)
        {
            Cdc.CanvasGetVectorTextBox(cobj, x, y, text, out xmin,out xmax, out ymin,out ymax);
        }

        public void GetVectorTextBoxW(double x, double y, string text, out double xmin, out double xmax, out double ymin, out double ymax)
        {
            Cdc.WCanvasGetVectorTextBox(cobj, x, y, text, out xmin, out xmax, out ymin, out ymax);
        }

        #endregion

        #region WORLD_COORDINATES

        public void SetWindow(double xmin,double xmax,double ymin,double ymax) {
            Cdc.WCanvasWindow(cobj,xmin,xmax,ymin,ymax);
        }

        public void GetWindow(out double xmin, out double xmax, out double ymin, out double ymax)
        {
            Cdc.WCanvasGetWindow(cobj, out xmin,out xmax,out ymin,out ymax);
        }

        /// <summary>
        /// Configures a viewport in the canvas coordinate system to be used to convert world coordinates (with values in real numbers) 
        /// into canvas coordinates (with values in integers). The default viewport is the whole canvas (0,w-1,0,h-1). 
        /// If the canvas size is changed, the viewport will not be automatically updated. 
        /// </summary>
        public void SetViewport(int xmin, int xmax, int ymin, int ymax)
        {
            Cdc.WCanvasViewport(cobj, xmin, xmax, ymin, ymax);
        }

        /// <summary>
        /// Queries the current viewport in the world coordinate system being used to convert world coordinates 
        /// into canvas coordinates (and the other way round).
        /// </summary>
        public void GetViewport(out int xmin, out int xmax, out int ymin, out int ymax)
        {
            Cdc.WCanvasGetViewport(cobj, out xmin, out xmax, out ymin, out ymax);
        }

        /// <summary>
        /// Converts world coordinates into canvas coordinates.
        /// </summary>
        public void WorldToCanvas(double xw, double yw, out int xv, out int yv)
        {
            Cdc.WCanvasWorld2Canvas(cobj, xw, yw, out xv, out yv);
        }

        /// <summary>
        /// Converts canvas coordinates into world coordinates.
        /// </summary>
        public void CanvasToWorld(int xv, int yv, out double xw, out double yw)
        {
            Cdc.WCanvasCanvas2World(cobj, xv, yv, out xw, out yw);
        }

        public void Hardcopy(Context context, IntPtr data, HardcopyCallback draw_func)
        {
            try
            {
                hardcopy_callback = draw_func;
                Cdc.WCanvasHardcopyPtr(cobj, context.context, data, InternalHardcopyCallback);
            }
            finally
            {
                hardcopy_callback = null;
            }

        }

        public void Hardcopy(Context context, string data, HardcopyCallback draw_func)
        {
            try
            {
                hardcopy_callback = draw_func;
                Cdc.WCanvasHardcopy(cobj, context.context, data, InternalHardcopyCallback);
            }
            finally
            {
                hardcopy_callback = null;
            }

        }


        void InternalHardcopyCallback(IntPtr canvas_copy)
        {
            Canvas tmp=new Canvas(canvas_copy);
            hardcopy_callback(tmp);
            tmp.cobj = IntPtr.Zero;    //cheat dispose system since cd c/c++ will delete this object
            tmp.Dispose();

        }

        #endregion

        #region COORDINATE_SYSTEMS

        public void GetSize(out int width, out int height, out double width_mm, out double height_mm)
        {
            Cdc.CanvasGetSize(cobj, out width, out height, out width_mm, out height_mm);
        }

        public void GetSize(out int width, out int height)
        {
            double width_mm, height_mm;
            Cdc.CanvasGetSize(cobj, out width, out height, out width_mm, out height_mm);
        }

        public int UpdateYAxis(out int y)
        {
            return Cdc.CanvasUpdateYAxis(cobj, out y);
        }

        public double UpdateYAxis(out double y)
        {
            return Cdc.FCanvasUpdateYAxis(cobj, out y);
        }

        public int InvertYAxis(int y)
        {
            return Cdc.CanvasInvertYAxis(cobj, y);
        }

        public double InvertYAxis(double y)
        {
            return Cdc.FCanvasInvertYAxis(cobj, y);
        }

        public void MM2Pixel(double mm_dx, double mm_dy, out int dx, out int dy)
        {
            Cdc.CanvasMM2Pixel(cobj, mm_dx, mm_dy, out dx, out dy);
        }

        public void MM2Pixel(double mm_dx, double mm_dy, out double dx, out double dy)
        {
            Cdc.FCanvasMM2Pixel(cobj, mm_dx, mm_dy, out dx, out dy);
        }

        public void Pixel2MM(double dx, double dy, out double mm_dx, out double mm_dy)
        {
            Cdc.FCanvasPixel2MM(cobj, dx, dy, out mm_dx, out mm_dy);
        }

        public void SetOrigin(int x, int y)
        {
            Cdc.CanvasOrigin(cobj, x, y);
        }

        public void SetOrigin(double x, double y)
        {
            Cdc.FCanvasOrigin(cobj, x, y);
        }

        public void GetOrigin(out int x, out int y)
        {
            Cdc.CanvasGetOrigin(cobj, out x, out y);
        }

        public void GetOrigin(out double x, out double y)
        {
            Cdc.FCanvasGetOrigin(cobj, out x, out y);
        }

        public Matrix2d Transform
        {
            get
            {
                IntPtr p=Cdc.CanvasGetTransform(cobj);
                Matrix2d res = new Matrix2d(p);
                return res;
            }

            set
            {
                Cdc.CanvasTransform(cobj, ref value.M[0]);
            }
        }

        public void TransformMultiply(Matrix2d mat)
        {
            Cdc.CanvasTransformMultiply(cobj,ref mat.M[0]);
        }

        public void Translate(double dx, double dy)
        {
            Cdc.CanvasTransformTranslate(cobj, dx, dy);
        }

        public void Scale(double sx, double sy)
        {
            Cdc.CanvasTransformScale(cobj, sx, sy);
        }

        public void Rotate(double degrees)
        {
            Cdc.CanvasTransformRotate(cobj, degrees);
        }

        public void TransformPoint(int x, int y, out int tx, out int ty)
        {
            Cdc.CanvasTransformPoint(cobj, x, y, out tx, out ty);
        }

        public void TransformPoint(double x, double y, out double tx, out double ty)
        {
            Cdc.FCanvasTransformPoint(cobj, x, y, out tx, out ty);
        }

               
        #endregion

        #region CLIPPING

        public ClipMode ClipMode
        {
            get
            {
                return (ClipMode)Cdc.CanvasClip(cobj, Query);
            }
            set
            {
                Cdc.CanvasClip(cobj, (int)value);
            }
        }

        public void ClipArea(int xmin, int xmax, int ymin, int ymax)
        {
            Cdc.CanvasClipArea(cobj, xmin, xmax, ymin, ymax);
        }

        public void ClipArea(double xmin, double xmax, double ymin, double ymax)
        {
            Cdc.FCanvasClipArea(cobj, xmin, xmax, ymin, ymax);
        }

        public void ClipAreaW(double xmin, double xmax, double ymin, double ymax)
        {
            Cdc.WCanvasClipArea(cobj, xmin, xmax, ymin, ymax);
        }

        public void GetClipArea(out int xmin, out int xmax, out int ymin, out int ymax)
        {
            Cdc.CanvasGetClipArea(cobj, out xmin, out xmax, out ymin, out ymax);
        }

        public void GetClipArea(out double xmin, out double xmax, out double ymin, out double ymax)
        {
            Cdc.FCanvasGetClipArea(cobj, out xmin, out xmax, out ymin, out ymax);
        }

        public void GetClipAreaW(out double xmin, out double xmax, out double ymin, out double ymax)
        {
            Cdc.WCanvasGetClipArea(cobj, out xmin, out xmax, out ymin, out ymax);
        }

        public RegionCombineMode RegionCombineMode
        {
            get
            {
                return (RegionCombineMode)Cdc.CanvasRegionCombineMode(cobj, Query);
            }
            set
            {
                Cdc.CanvasRegionCombineMode(cobj, (int)value);
            }
        }

        public bool IsPointInRegion(int x, int y)
        {
            return Cdc.CanvasIsPointInRegion(cobj, x, y) != 0;
        }

        public void OffsetRegion(int dx, int dy)
        {
            Cdc.CanvasOffsetRegion(cobj, dx, dy);
        }

        public void OffsetRegionW(double dx, double dy)
        {
            Cdc.WCanvasOffsetRegion(cobj, dx, dy);
        }

        public void GetRegionBox(out int xmin, out int xmax, out int ymin, out int ymax)
        {
            Cdc.CanvasGetRegionBox(cobj, out xmin, out xmax, out ymin, out ymax);
        }

        public void GetRegionBoxW(out double xmin, out double xmax, out double ymin, out double ymax)
        {
            Cdc.WCanvasGetRegionBox(cobj, out xmin, out xmax, out ymin, out ymax);
        }

      
        #endregion

        #region CLIENT_IMAGES

       

        public void PutImage(ClientImage img,int x,int y)
        {
            switch (img.Type)
            {
                case ClientImageType.RGB:
                    Cdc.CanvasPutImageRectRGB(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], x, y, 0, 0, 0, 0, 0, 0);
                    break;
                case ClientImageType.RGBA:
                    Cdc.CanvasPutImageRectRGBA(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], ref img.Alpha[0], x, y, 0, 0, 0, 0, 0, 0);
                    break;
                case ClientImageType.Map:
                    Cdc.CanvasPutImageRectMap(cobj, img.Width, img.Height, ref img.Map[0], ref img.Colors[0], x, y, 0, 0, 0, 0, 0, 0);
                    break;
            }

        }

        public void PutImageW(ClientImage img, double x, double y)
        {
            switch (img.Type)
            {
                case ClientImageType.RGB:
                    Cdc.WCanvasPutImageRectRGB(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], x, y, 0, 0, 0, 0, 0, 0);
                    break;
                case ClientImageType.RGBA:
                    Cdc.WCanvasPutImageRectRGBA(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], ref img.Alpha[0], x, y, 0, 0, 0, 0, 0, 0);
                    break;
                case ClientImageType.Map:
                    Cdc.WCanvasPutImageRectMap(cobj, img.Width, img.Height, ref img.Map[0], ref img.Colors[0], x, y, 0, 0, 0, 0, 0, 0);
                    break;
            }
        }

        public void PutImage(ClientImage img, int x, int y, int w, int h)
        {
            switch (img.Type)
            {
                case ClientImageType.RGB:
                    Cdc.CanvasPutImageRectRGB(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], x, y, w, h, 0, 0, 0, 0);
                    break;
                case ClientImageType.RGBA:
                    Cdc.CanvasPutImageRectRGBA(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], ref img.Alpha[0], x, y, w, h, 0, 0, 0, 0);
                    break;
                case ClientImageType.Map:
                    Cdc.CanvasPutImageRectMap(cobj, img.Width, img.Height, ref img.Map[0], ref img.Colors[0], x, y, w, h, 0, 0, 0, 0);
                    break;
            }
        }

        public void PutImageW(ClientImage img, double x, double y, double w, double h)
        {
            switch (img.Type)
            {
                case ClientImageType.RGB:
                    Cdc.WCanvasPutImageRectRGB(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], x, y, w, h, 0, 0, 0, 0);
                    break;
                case ClientImageType.RGBA:
                    Cdc.WCanvasPutImageRectRGBA(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], ref img.Alpha[0], x, y, w, h, 0, 0, 0, 0);
                    break;
                case ClientImageType.Map:
                    Cdc.WCanvasPutImageRectMap(cobj, img.Width, img.Height, ref img.Map[0], ref img.Colors[0], x, y, w, h, 0, 0, 0, 0);
                    break;
            }
        }

        public void PutImage(ClientImage img, int x, int y, int w, int h,int src_xmin,int src_xmax,int src_ymin,int src_ymax)
        {
            switch (img.Type)
            {
                case ClientImageType.RGB:
                    Cdc.CanvasPutImageRectRGB(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], x, y, w, h, src_xmin,src_xmax,src_ymin,src_ymax);
                    break;
                case ClientImageType.RGBA:
                    Cdc.CanvasPutImageRectRGBA(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], ref img.Alpha[0], x, y, w, h, src_xmin, src_xmax, src_ymin, src_ymax);
                    break;
                case ClientImageType.Map:
                    Cdc.CanvasPutImageRectMap(cobj, img.Width, img.Height, ref img.Map[0], ref img.Colors[0], x, y, w, h, src_xmin, src_xmax, src_ymin, src_ymax);
                    break;
            }
        }

        public void PutImageW(ClientImage img, double x, double y, double w, double h, int src_xmin, int src_xmax, int src_ymin, int src_ymax)
        {
            switch (img.Type)
            {
                case ClientImageType.RGB:
                    Cdc.WCanvasPutImageRectRGB(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], x, y, w, h, src_xmin, src_xmax, src_ymin, src_ymax);
                    break;
                case ClientImageType.RGBA:
                    Cdc.WCanvasPutImageRectRGBA(cobj, img.Width, img.Height, ref img.Red[0], ref img.Green[0], ref img.Blue[0], ref img.Alpha[0], x, y, w, h, src_xmin, src_xmax, src_ymin, src_ymax);
                    break;
                case ClientImageType.Map:
                    Cdc.WCanvasPutImageRectMap(cobj, img.Width, img.Height, ref img.Map[0], ref img.Colors[0], x, y, w, h, src_xmin, src_xmax, src_ymin, src_ymax);
                    break;
            }
        }

      
        #endregion

        #region SERVER_IMAGES
        public void PutImage(ServerImage img, int x, int y)
        {
            Cdc.CanvasPutImageRect(cobj,img.cobj,x,y,0,0,0,0);
        }

        public void PutImageW(ServerImage img, double x, double y)
        {
            Cdc.WCanvasPutImageRect(cobj, img.cobj, x, y, 0, 0, 0, 0);
        }

        public void PutImage(ServerImage img, int x, int y, int src_xmin, int src_xmax, int src_ymin, int src_ymax)
        {
            Cdc.WCanvasPutImageRect(cobj, img.cobj, x, y, src_xmin, src_xmax, src_ymin, src_ymax);
        }

        public void PutImageW(ServerImage img, double x, double y, int src_xmin, int src_xmax, int src_ymin, int src_ymax)
        {
            Cdc.WCanvasPutImageRect(cobj, img.cobj, x, y, src_xmin, src_xmax, src_ymin, src_ymax);
        }


        /// <summary>
        /// Copies the rectangle defined by the coordinates (xmin,ymin) and (xmax,ymax) to the rectangle defined by  (xmin+dx,ymin+dy) 
        /// and (xmax+dx,ymax+dy). It has the same effect as ServerImage.Copy followed by PutImage, but it should be faster and does not 
        /// require the explicit creation of an server image to be executed. 
        /// Note that the region belonging to the first rectangle, but not to the second, remains unchanged (the function does not clean this region). 
        /// </summary>
        public void ScrollArea(int xmin, int xmax, int ymin, int ymax, int dx, int dy)
        {
            Cdc.CanvasScrollArea(cobj, xmin, xmax, ymin, ymax, dx, dy);

        }

        #endregion

        #region COLOR_CODING

        //color coding

        public static int EncodeColor(byte red, byte green, byte blue)
        {
            return Cdc.EncodeColor(red, green, blue);
        }


        public static void DecodeColor(int color, out byte red, out byte green, out byte blue)
        {
            Cdc.DecodeColor(color, out red, out green, out blue);
        }

        public static int EncodeAlpha(int color, byte alpha)
        {
            return Cdc.EncodeAlpha(color, alpha);
        }

        public static byte DecodeAlpha(int color)
        {
            return Cdc.DecodeAlpha(color);
        }

        public static byte Alpha(int color)
        {
            return Cdc.Alpha(color);
        }

        public static byte Red(int color)
        {
            return Cdc.Red(color);
        }

        public static byte Green(int color)
        {
            return Cdc.Green(color);
        }

        public static byte Blue(int color)
        {
            return Cdc.Blue(color);
        }

        public int ColorPlanes
        {
            get
            {
                return Cdc.CanvasGetColorPlanes(cobj);
            }
        }

        public void SetPalette(int[] colors, PaletteMode mode)
        {
            Cdc.CanvasPalette(cobj, Math.Max(256, colors.Length), ref colors[0], (int)mode);
        }

        #endregion

        #region CUSTOM_FUNCTIONS

        private Matrix2d WorldToCanvasMatrix
        {
            get
            {
                double winx1, winx2, winy1, winy2;
                int vpx1, vpy1, vpx2, vpy2;

                GetWindow(out winx1, out winx2, out winy1, out winy2);
                GetViewport(out vpx1, out vpx2, out vpy1, out vpy2);

                double sx = (double)(vpx2 - vpx1) / (winx2 - winx1);
                double sy = (double)(vpy2 - vpy1) / (winy2 - winy1);

                //create matrix converting from world to canvas space
                return new Matrix2d(
                    sx,
                    0.0,
                    0.0,
                    sy,
                    vpx1 - winx1 * sx,
                    vpy1 - winy1 * sy
                );
            }
        }

        public void TransformMultiplyW(Matrix2d mat)
        {
            Matrix2d w2c = WorldToCanvasMatrix;

            TransformMultiply(w2c.Inverse * mat * w2c);
        }

        public Matrix2d TransformW
        {
            set
            {
                Matrix2d w2c = WorldToCanvasMatrix;
                Transform = w2c.Inverse * value * w2c;
            }
            get
            {
                Matrix2d w2c = WorldToCanvasMatrix;
                return w2c.Inverse * Transform * w2c;
            }
        }

        public void TranslateW(double dx,double dy)
        {
            TransformMultiplyW(Matrix2d.Translate(dx,dy));
        }

        public void RotateW(double degrees)
        {
            double rad = degrees * (Math.PI / 180.0);
            TransformMultiplyW(Matrix2d.Rotate(rad));
        }

        public void ScaleW(double sx,double sy)
        {
            TransformMultiplyW(Matrix2d.Stretch(sx,sy));
        }

        #endregion


    }
}
